import os
import time
import random
import requests
from bs4 import BeautifulSoup
class TieBa_Crawler(object):
    '''贴吧爬虫类'''
    # 贴吧网站地址
    base_url = "https://tieba.baidu.com"
    def __init__(self,name):
        # 初始化贴吧名称
        self.name = name
        # 初始化贴吧地址
        self.url = f'{TieBa_Crawler.base_url}/f?kw={self.name}'
        # 初始化爬取页面数量
        self.pages = 1
        # 初始化浏览器类型
        self.headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"}
def get_forum_page(self,page_num=1):
        '''获取贴吧帖子列表页面'''
        # pn参数： （页码-1）*50, 默认情况pn=0获取首页
        pn = (page_num-1)*50
        # url: 形如 https://tieba.baidu.com/f?kw=重庆科技学院&pn=0
        url = self.url + f'&pn={pn}'
        # 发送GET请求获取服务器响应对象
        response = requests.get(url)
        # 返回响应文本
        return response.text

def get_post_list(self,html_content):
        '''解析贴吧页面，获取帖子链接和标题的列表'''
        # 使用BeautifulSoup的对贴吧页面的html文本进行解析，获的soup对象
        soup = BeautifulSoup(html_content,"html.parser")
        # 初始化帖子列表
        post_list = []
        # 在soup对象中查找class="j_th_tit "的元素
        for post in soup.find_all('a', class_="j_th_tit"):
            # 获取帖子链接
            post_href = post.attrs.get('href')
            # 获取帖子标题
            post_title = post.attrs.get('title')
            # 将帖子链接和标题存放在帖子列表中
            post_list.append((post_href,post_title))
        # 返回帖子列表
        return post_list
def __init__(self,name):
        # 初始化贴吧名称
        self.name = name
        # 初始化贴吧地址
        self.url = f'{TieBa_Crawler.base_url}/f?kw={self.name}'
        # 初始化爬取页面数量
        self.pages = 1
        # 初始化浏览器类型
        self.headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"}
        # 初始化只看楼主功能 （0关闭，1开启）
        self.seeLZ = 0
def get_post_page(self, post_url, page_num=1):
    '''获取帖子内容页面的方法'''
    # pn参数： 当前帖子的回帖页数
    # seeLZ： 是否只显示楼主的发帖
    url = post_url+f'?pn={page_num}&see_lz={self.seeLZ}'
    response = requests.get(url=url, headers=self.headers)
    # 此处我们使用response.content获取页面响应的二进制内容，注意与response.text的区别
    return response.content
def get_post_page_num(self,html_content):
    '''获取帖子内容页面的数量'''
    # 默认帖子页面为1页
    post_page_num = 1
    # 使用BeautifulSoup解析帖子页面的html响应文本，得到soup对象
    soup = BeautifulSoup(html_content, "html.parser")
    # 查找所有的l_reply_num列表元素
    for l_reply_num in soup.find_all('li',class_="l_reply_num"):
        # 如果帖子有多页，那么存在一个输入页码的input控件
        if l_reply_num.find('input'):
            # 获取该input控件的max-page属性，即该贴存在页面的总数量
            post_page_num = l_reply_num.find('input').attrs.get('max-page')
    # 以数值类型返回这个数量
    return int(post_page_num)
def get_post_text(self,html_content):
    '''解析帖子内容页面，将各楼层文本信息存放在列表中返回'''
    # 使用BeautifulSoup的对贴吧内容页面的html文本进行解析，获的soup对象
    soup = BeautifulSoup(html_content, "html.parser")
    # 初始化post_content列表，用于存放各楼层发帖本文
    post_content = []
    # 搜索所有的发帖楼层的html元素
    for d_post_content in soup.find_all('div', class_="d_post_content j_d_post_content clearfix"):
        # 获取楼层内的文本信息
        post_text = d_post_content.get_text().strip()
        # 将该楼层文本信息存放如内容列表
        post_content.append(post_text)
    # 返回包含该页面所有楼层内容的列表
    return post_content
def save_post_text(self,post_title, post_content):
    '''保存帖子内容到文件'''
    # 以贴吧名称作为文件存放的目录
    if not os.path.exists(self.name):
        os.mkdir(self.name)
    # 以帖子标题.txt作为文件名，保存到贴吧名目录下
    filepath = os.path.join(self.name,post_title+'.txt')
    # 打开帖子标题.txt文件，并写入各楼层内容
    with open(filepath,mode='a',encoding= 'utf-8') as f:
        for num,text in enumerate(post_content):
            f.write(f"{num}楼".center(50,'-'))
            f.write("\n")
            f.write(text)
            f.write("\n")
def run(self, pages=1):
    '''爬虫运行函数'''
    # 设置抓取页面数量（每页50贴）
    self.pages = pages
    # 对于每一页循环
    for cur_forum_page in range(self.pages):
        # 首先获取贴吧列表页面的html文本
        forum_page_html = cqust_tieba.get_forum_page(cur_forum_page)
        # 从贴吧列表页面获取当前页所有帖子的相对链接和标题
        post_list = cqust_tieba.get_post_list(forum_page_html)
        # 对每一个帖子循环
        for post_url, post_title in post_list:
            # 使用贴吧基本地址和帖子相对链接组合成帖子链接
            post_url = TieBa_Crawler.base_url + post_url
            # 从第1页开始
            cur_page = 1
            # 获取帖子html文本
            post_page_html = cqust_tieba.get_post_page(post_url, page_num=cur_page).decode('utf-8')
            # 获取帖子各楼层文本列表
            post_text_list = cqust_tieba.get_post_text(post_page_html)
            # 获取帖子页面数量
            post_page_num = cqust_tieba.get_post_page_num(post_page_html)
            # 完成一次抓取后暂停随机1.x秒，避免频繁抓取触发反爬验证机制
            time.sleep(1+random.random())
            # 如果存在多页
            while cur_page < post_page_num:
                # 抓取下一页
                cur_page += 1
                # 获取帖子html文本
                post_page = cqust_tieba.get_post_page(post_url, page_num=cur_page).decode('utf-8')
                # 将后续页的各楼层文本追加到列表中
                post_text_list += cqust_tieba.get_post_text(post_page)
                # 完成一次抓取后暂停随机1.x秒，避免频繁抓取触发反爬验证机制
                time.sleep(1+random.random())
            # 将每一个帖子所有页面的楼层内容列表写入文件中
            cqust_tieba.save_post_text(post_title, post_text_list)
            # 控制台输出当前抓取完毕的帖子信息
            print(post_url, post_title, post_page_num)
            # 完成一次抓取后暂停随机3~5秒，避免频繁抓取触发反爬验证机制
            time.sleep(3 + 2 * random.random())
if __name__ == '__main__':
    # 使用贴吧名称创建一个贴吧爬虫
    cqust_tieba = TieBa_Crawler('重庆科技学院')
    # 运行这个贴吧爬虫，run方法可传入页面抓取数量，默认为1页
    cqust_tieba.run()